Release 10.1A: OpenEdge Development:
ProDataSets
Using event procedures in the sample procedure
Let’s move some of the supporting code to event procedures to test the callback facility.
![]()
To modify the code:
- Create a new procedure called
OrderMain.pthat acts as the defining procedure for the ProDataSet, as shown below.
This new procedure simply defines the ProDataSet, accepts the
Ordernumber, and then runs a new event procedure where all the rest of the work is done. It passes theOrdernumber and the ProDataSet handle in asINPUTparameters.OrderEvents.pbinds the supporting events to the ProDataSet handle passed in as part of its main block, using theSET-CALLBACK-PROCEDUREmethod.OrderMainthen does aFILLon the ProDataSet, which triggers the various events inOrderEvents.p. Finally, it deletes the persistent event procedure. In a real application, of course, it is likely that you would start event procedures like this one when you first need them and then leave them running to serve any caller.- Modify
dsOrderWin.wto run this new procedure instead offillDSOrder.pin theLEAVEtrigger foriOrderNum.- Create the event handling procedure
OrderEvents.p. Include the temp-table and ProDataSet definitions, and define the twoINPUTparameters it needs:
There is a rule that states you cannot define a static ProDataSet parameter at the top main block level of a procedure that you are going to run persistent, like this one. This is because Progress needs an enclosing procedure block to pass a static ProDataSet reference into a persistent procedure by reference. For this reason, only internal procedures can have a static ProDataSet parameter. The static ProDataSet definition in
dsOrder.iis used in these internal procedures later on, but the initial parameter at the top-level must be just a ProDataSet handle.- You need your top-level
Orderquery definition, which you use to prepare a query for theOrdernumber passed in:
- A couple of variables are needed to identify temp-table buffers based on the ProDataSet handle:
- Following the variables, Data-Source definitions from the first test procedure for
dsOrderare found:
- The main block of the procedure establishes all the callbacks, so that when
OrderMain.pdoes itsFILL, they will be ready to respond to the events that happen as theOrder,OrderLine, andItemrecords are read in and temp-table records are created for them. The first two callbacks are for the start and end of the entireFILLat the level of the ProDataSet, so they are executed on the ProDataSet handle itself:
The first of these procedures, which you’ll define in a moment, prepares the
Orderquery. The second one detaches all the Data-Sources.The remaining callbacks attach procedures to the temp-table buffers. Since the temp-table and ProDataSet definitions are included in the
OrderEvents.p, it is natural to think that you can simply reference a buffer such asttOrderin the callback definition:
Let’s explore why this can’t work the way you might expect it to. The code in
preOrderFillattaches all the Data-Sources to the buffer. This is what thepreOrderFillevent procedure looks like:
The
SET-CALLBACK-METHODmethod along with its event procedure compiles just fine, because there is indeed a localttOrderbuffer the compiler can refer to. But before we go any further, we’ll show you what will happen if you run the window with the callbacks defined in this way:
![]()
This message is telling you that Progress was unable to fill the ProDataSet because when it got to the first table,
ttOrder, there was no Data-Source for it, and there was also no callback procedure to take the place of the Data-Source and to do the table fill itself. But the code defines a callback procedure, and the callback procedure attaches the Data-Sources. So what went wrong?The answer is the same as in the example which showed the side effects of ProDataSets passed
BY-REFERENCE ("Local parameter passing example" section). ThettOrderbuffer definition in theSET-CALLBACK-PROCEDUREmethod in the main block has no relationship to thettOrderbuffer for the ProDataSet handlephDataSetpassed intoOrderEvents.p. The ProDataSet definition indsOrder.iand its temp-table definitions indsOrderTT.iare strictly local at this point, and define what amounts to a separate instance of the same temp-tables and ProDataSet. Thus, when the code is attached to a callback toBUFFERttOrder, it’s attaching it to a handle for a temp-table the procedure isn’t really using and that the caller isn’t aware of.- To get the right buffer handle from the ProDataSet handle, you need to use one of the ProDataSet methods,
GET-BUFFER-HANDLE, to access the buffer handle through the ProDataSet handle. This is the correct block of code that the main block ofOrderEvents.pmust use to attach the remaining callback events:
You’ll learn about all the ProDataSet methods and attributes in following chapters. For now, let’s look at all the remaining callback procedures.
- The first one is for the
BEFORE-FILLevent of the ProDataSet itself. It prepares theOrderquery based on theOrderNumthat was passed in toOrderEvents.p:
Remember that this procedure isn’t run when
OrderEvents.pis run, but only later when theFILLevent occurs. ThepiOrderNumparameter value is still available only because in this simple example the callback is only used by one caller, and its value is set when the persistent procedure is first run. In a real application you should construct your callbacks so that they can be shared by multiple instances of the objects that use them.- The second procedure is the
AFTER-FILLevent for the ProDataSet. It detaches all the Data-Sources, again using theNUM-BUFFERSattribute and theGET-BUFFER-HANDLEmethod to walk through the ProDataSet:
You’ve seen the first buffer-level callback procedure,
preOrderFill, which is theBEFORE-FILLevent for thettOrdertable. Take another look at the first lines of this procedure:
If it wasn’t correct to refer to
BUFFERttOrderin theSET-CALLBACK-PROCEDUREmethod in the main block, then why is it correct to do it here?The answer to this is one of the key points you must keep in mind as you build applications with ProDataSets. The internal procedure pre
Design tip: Always keep in mind as you develop your applications whether you have a local ProDataSet, a reference to a ProDataSet defined in another procedure, or simply a handle to a ProDataSet.OrderFill receives a static reference to the ProDataSetdsOrderas anINPUTparameter. This is valid because you can pass a static ProDataSet reference to an internal procedure, whereas you cannot pass a static ProDataSet reference to the main block of a persistent procedure such asOrderEvents.p. Because Progress passes the ProDataSetdsOrderintopreOrderFillby reference, it simply points this internal procedure to the instance ofdsOrderdefined in the calling procedure. The local temp-table and ProDataSet definitions indsOrder.ianddsOrderTT.ithat the compiler uses to compile the reference toBUFFERttOrderare automatically mapped, at the time the internal procedure is run, to a completely separate temp-table and ProDataSet definition. Therefore, within this internal procedure, the expressionBUFFERttOrderrefers correctly to the buffer handle of thettOrdertemp-table, which is part of the ProDataSetdsOrderthat is passed into the procedure. By contrast, in the main block the same reference is not correct because the only thing available to the main block is the handle of the caller’s ProDataSet, not the ProDataSet itself. This is very important to understand as you start to work with ProDataSets.The callback for the
AFTER-FILLevent on thettOlinebuffer calculates the extra fieldOrderTotalin thettOrderrecord, as shown:
There are two more important points that this very simple calculation illustrates:
Design tip: Always write your ProDataSet business logic to use the temp-table definitions in your ProDataSets wherever possible, because this is the definition that should remain constant and consistent regardless of how the mapping to the underlying database tables or other data source might change.- Since the
ttOlinetable has just been filled with all theOrderLinesfor theOrder, the code can refer to the temp-table rather than the database records. This helps you write business logic that refers to the internal definition of your data, as distinct from the details of how it’s stored in the database. If you change the nature of the mapping between theOrderLinedata in the database and the fields in thettOlinetable in the future, or even replace it with a completely different data source, the code that does the calculation doesn’t need to change.Design tip: Always remember that all the contents of the ProDataSet are available to you in every event procedure. You can freely refer to parent records of the current table, and the buffer for the parent table for an event executed during a- The
ttOrderrecord for the currentttOlinesis immediately available to you because of the way in which Progress executes the fill. For eachttOrderit populates, it immediately goes down a level in the relations and fills the children of that parent. This buffer currency is available to you even here where the local temp-table definitions forttOrderandttOlineare actually pointing to a ProDataSet defined elsewhere.FILLwill hold the parent record for the current children that triggered the event. Once theFILLis complete, a ProDataSet reference can give you access to any data in any of the ProDataSet’s tables.- The final procedure is different from the others in that it is executed at the level of a single row fill, the
AFTER-ROW-FILLevent for thettItemtable. The procedure is executed once for every row in thettItemtable, just after the row is created and the fields from theItemData-Source copied in. For example:
This bit of code looks at the
ItemName field, identifies whether it contains one of several key strings such asBASEBALLorCROQUET, and highlights those strings by replacing the string in the name with all uppercase. This is a simple illustration of the usefulness of the row-level event. You can use it to fill in calculated fields, to filter records beyond the default record selection, and for other row-level purposes.- Now if you run the
OrderProDataSet window, you can see first of all that the code to attach Data-Sources and other such things that was moved into the event procedures executes correctly. In addition, the special event processing code that calculates theOrderTotalfield and highlights the key words in theItemNameare working as well:
![]()
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |